using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using Workflows.Diagrams.Properties;
using Northwoods.Go;

namespace Workflows.Diagrams
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class FlowLink : GoLabeledLink
    {
        #region members
        public event UpdateProperty UpdatePropertyEvent;
        #endregion

        #region ctor
        /// <summary>
        /// 
        /// </summary>
        public FlowLink()
        {
            this.Orthogonal = true;
            this.AvoidsNodes = true;
            this.Style = GoStrokeStyle.RoundedLineWithJumpOvers;
            Pen p = new Pen(Color.Blue, 2);
            this.Pen = p;
            this.HighlightPen = new Pen(Color.White, 6);
            this.Highlight = true;
            LinkLabel t = new LinkLabel();
            this.Add(t);

            GoImage goImg = new GoImage();
            goImg.Image = Resources.suspend;
            goImg.Width = 15;
            goImg.Height = 15;
            goImg.Selectable = false;
            this.Add(goImg);
        }
        #endregion

        #region properties
        /// <summary>
        /// 
        /// </summary>
        public string Text
        {
            get
            {
                GoText label = this.Label;
                if (label != null)
                {
                    return label.Text;
                }
                else
                {
                    return string.Empty;
                }
            }
            set
            {
                GoText label = this.Label;
                if (label != null)
                {
                    label.Text = value;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public LinkLabel Label
        {
            get
            {
                foreach (GoObject obj in this)
                {
                    LinkLabel label = null;
                    if (obj.GetType() == typeof(LinkLabel))
                        label = (LinkLabel)obj;

                    if (label != null)
                        return label;
                }
                return null;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public GoImage Image
        {
            get
            {
                foreach (GoObject obj in this)
                {
                    if (obj.GetType() == typeof(GoImage))
                        return (GoImage) obj;
                }
                return null;
            }
        }
        #endregion

        #region overrides
        /// <summary>
        /// 
        /// </summary>
        /// <param name="childchanged"></param>
        public override void LayoutChildren(GoObject childchanged)
        {
            if (this.Initializing)
                return;

            base.LayoutChildren(childchanged);

            foreach (GoObject child in this)
            {
                IGoLinkLabel l = child as IGoLinkLabel;
                if (l != null)
                    this.PositionLinkLabel(l, childchanged);

                GoImage goImg = child as GoImage;
                if (goImg != null)
                    this.PositionGoImage(goImg);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sel"></param>
        public override void OnGotSelection(GoSelection sel)
        {
            base.OnGotSelection(sel);
            this.SkipsUndoManager = true;
            Color c;
            if (sel.Primary.Equals(this))
                c = sel.View.PrimarySelectionColor;
            else
                c = sel.View.SecondarySelectionColor;

            this.HighlightPen = new Pen(c, 6);
            this.SkipsUndoManager = false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sel"></param>
        public override void OnLostSelection(GoSelection sel)
        {
            base.OnLostSelection(sel);
            this.SkipsUndoManager = true;
            this.HighlightPen = new Pen(Color.White, 6);
            this.SkipsUndoManager = false;
        }

        //public override bool OnContextClick(GoInputEventArgs evt, GoView view)
        //{
        //    GoContextMenu cm = new GoContextMenu(view);
        //    cm.MenuItems.Add(new MenuItem("Properties", new EventHandler(this.Properties_Command)));
        //    cm.MenuItems.Add(new MenuItem("Insert Point", new EventHandler(this.InsertPoint_Command)));
        //    if (this.CanRemovePoint())
        //        cm.MenuItems.Add(new MenuItem("Remove Segment", new EventHandler(this.RemoveSegment_Command)));
        //    cm.Show(view, evt.ViewPoint);
        //    return true;
        //}
        #endregion

        #region context menu event handler
        //public void Properties_Command(object sender, EventArgs e)
        //{
        //    GoView v = GoContextMenu.FindView((MenuItem)sender);
        //    if (v != null)
        //    {
        //        v.Selection.Select(this);
        //        this.ShowProperties(this);
        //    }
        //}

        //public void ShowProperties(GoObject obj)
        //{
        //    if (this.UpdatePropertyEvent != null)
        //        this.UpdatePropertyEvent(obj);
        //}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void InsertPoint_Command(object sender, EventArgs e)
        {
            GoView v = GoContextMenu.FindView((MenuItem)sender);
            if (v != null)
            {
                PointF menuPt = v.LastInput.DocPoint;
                GoStroke s = this.RealLink;
                int i = s.GetSegmentNearPoint(menuPt);
                if (s.PointsCount > 3)
                {
                    if (i < 1)
                        i = 1;
                    else if (i >= s.PointsCount - 2)
                        i = s.PointsCount - 3;
                }
                PointF closest;
                if (GoStroke.NearestPointOnLine(s.GetPoint(i), s.GetPoint(i + 1), menuPt, out closest))
                {
                    v.StartTransaction();
                    s.InsertPoint(i + 1, closest);
                    if (this.Orthogonal)
                        s.InsertPoint(i + 1, closest);
                    s.AddSelectionHandles(v.Selection, this);
                    v.FinishTransaction("Inserted point into link stroke");
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool CanRemovePoint()
        {
            if (this.Orthogonal)
                return (this.RealLink.PointsCount > 6);
            else
                return (this.RealLink.PointsCount > 2);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void RemoveSegment_Command(object sender, EventArgs e)
        {
            GoView v = GoContextMenu.FindView((MenuItem)sender);
            if (v != null)
            {
                PointF menuPt = v.LastInput.DocPoint;
                GoStroke s = this.RealLink;
                int i = s.GetSegmentNearPoint(menuPt);
                v.StartTransaction();
                if (this.Orthogonal)
                {
                    i = Math.Max(i, 2);
                    i = Math.Min(i, this.RealLink.PointsCount - 5);
                    PointF a = s.GetPoint(i);
                    PointF b = s.GetPoint(i + 1);
                    s.RemovePoint(i);
                    s.RemovePoint(i);
                    PointF c = s.GetPoint(i);
                    if (a.X == b.X)
                        c.Y = a.Y;
                    else
                        c.X = a.X;
                    s.SetPoint(i, c);
                }
                else
                {
                    i = Math.Max(i, 1);
                    GoPort port = null;
                    if (this.FromPort.GetType() == typeof(GoPort))
                        port = (GoPort)this.FromPort;
                    if (port != null && port.FromSpot != NoSpot)
                        i = Math.Max(i, 2);
                    i = Math.Min(i, this.RealLink.PointsCount - 2);
                    port = null;
                    if (this.ToPort.GetType() == typeof(GoPort))
                        port = (GoPort)this.ToPort;
                    if (port != null && port.ToSpot != NoSpot)
                        i = Math.Min(i, this.RealLink.PointsCount - 3);
                    s.RemovePoint(i);
                }
                s.AddSelectionHandles(v.Selection, this);
                v.FinishTransaction("Removed point from link stroke");
            }
        }

        #endregion

        #region private methods
        private void PositionLinkLabel(IGoLinkLabel l, GoObject childChanged)
        {
            if (l == null)
                return;

            if (l == childChanged)
            {
                PointF center = childChanged.Center;
                GoStroke s = this.RealLink;
                Single closestDist = 1.0E+21F;
                PointF closestPt = new PointF();
                int closestSeg = -1;
                int numpts = s.PointsCount;
                for (int i = 0; i <= (numpts - 2); i++)
                {
                    PointF start = s.GetPoint(i);
                    PointF end = s.GetPoint(i + 1);
                    PointF r;
                    GoStroke.NearestPointOnLine(start, end, center, out r);
                    Single dist = ((r.X - center.X) * (r.X - center.X) + (r.Y - center.Y) * (r.Y - center.Y));
                    if (dist < closestDist)
                    {
                        closestDist = dist;
                        closestPt = r;
                        closestSeg = i;
                    }
                }

                if (closestSeg > -1)
                {
                    l.Offset = new SizeF(center.X - closestPt.X, center.Y - closestPt.Y);
                    l.Segment = closestSeg;
                    PointF a = s.GetPoint(closestSeg);
                    PointF b = s.GetPoint(closestSeg + 1);
                    PointF r = closestPt;
                    double rdist = Math.Sqrt((a.X - r.X) * (a.X - r.X) + (a.Y - r.Y) * (a.Y - r.Y));
                    double sdist = Math.Sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y));
                    if (sdist <= 0)
                        l.SegmentPercentage = 0;
                    else
                        l.SegmentPercentage = (Single)(rdist * 100 / sdist);
                }
            }
            else
            {
                SizeF off = l.Offset;
                PointF cp = l.LinkLabelConnnectionPoint;
                ((GoObject)l).Center = new PointF(cp.X + off.Width, cp.Y + off.Height);
            }
        }

        private void PositionGoImage(GoImage goImg)
        {
            if (goImg == null)
                return;

            for(int i=0;i<this.Count;i++)
            {
                IGoLinkLabel lbl = this[i] as IGoLinkLabel;
                if(lbl != null)
                {
                    PointF cp = lbl.LinkLabelConnnectionPoint;
                    // SizeF off = lbl.Offset;
                    goImg.Center = cp;
                    break;
                }
            }
        }
        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="enable"></param>
        public void EnableFilter(bool enable)
        {
            for(int i=0;i<this.Count;i++)
            {
                if(this[i].GetType()==typeof(GoImage))
                {
                    GoImage goImg = (GoImage) this[i];
                    goImg.Visible = enable;
                }
            }
        }
    }
}
